//基因序列可以表示为一条由 8 个字符组成的字符串，其中每个字符都是 'A'、'C'、'G' 和 'T' 之一。 
//
// 假设我们需要调查从基因序列 start 变为 end 所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。 
//
// 
// 例如，"AACCGGTT" --> "AACCGGTA" 就是一次基因变化。 
// 
//
// 另有一个基因库 bank 记录了所有有效的基因变化，只有基因库中的基因才是有效的基因序列。 
//
// 给你两个基因序列 start 和 end ，以及一个基因库 bank ，请你找出并返回能够使 start 变化为 end 所需的最少变化次数。如果无法完成
//此基因变化，返回 -1 。 
//
// 注意：起始基因序列 start 默认是有效的，但是它并不一定会出现在基因库中。 
//
// 
//
// 示例 1： 
//
// 
//输入：start = "AACCGGTT", end = "AACCGGTA", bank = ["AACCGGTA"]
//输出：1
// 
//
// 示例 2： 
//
// 
//输入：start = "AACCGGTT", end = "AAACGGTA", bank = ["AACCGGTA","AACCGCTA","AAACGG
//TA"]
//输出：2
// 
//
// 示例 3： 
//
// 
//输入：start = "AAAAACCC", end = "AACCCCCC", bank = ["AAAACCCC","AAACCCCC","AACCCC
//CC"]
//输出：3
// 
//
// 
//
// 提示： 
//
// 
// start.length == 8 
// end.length == 8 
// 0 <= bank.length <= 10 
// bank[i].length == 8 
// start、end 和 bank[i] 仅由字符 ['A', 'C', 'G', 'T'] 组成 
// 
// Related Topics 广度优先搜索 哈希表 字符串 
// 👍 173 👎 0

package com.cute.leetcode.editor.cn;

import java.util.*;

public class MinimumGeneticMutation {
    public static void main(String[] args) {
        String start = "AACCGGTT";
        String end = "AAACGGTA";
        String[] bank = {"AACCGATT","AACCGATA","AAACGATA","AAACGGTA"};
        new MinimumGeneticMutation().new Solution().minMutation(start, end, bank);
    }
    //leetcode submit region begin(Prohibit modification and deletion)
    class Solution {
        // 试试反向变换呢，emmmm我这个破办法纯纯的凑出来了
        List<String> list;
        Set<String> used;
        public int minMutation1(String start, String end, String[] bank) {
            list = new ArrayList<>();
            list.addAll(Arrays.asList(bank));
            used = new HashSet<>();
            if (!list.contains(end)) {
                return -1;
            }
            return dfs(start, end, list, used);
        }

        // 使用dfs进行递归查找
        private int dfs(String start, String cur, List<String> bank, Set<String> used) {
            // 此时说明目标串和当前串已经差最多一个了，可以直接返回
            if (valid(cur, start) < 2) return valid(cur, start);

            // 添加元素到已访问集合
            used.add(cur);
            int min = 10;// 记录最小值
            for (String next : bank){
                if (!used.contains(next) && valid(next, cur) < 2){ // 遍历所有剩余串
                    int temp = dfs(start, next, bank, used);
                    if (temp != -1) min = Math.min(min, temp);
                }
            }
            // 删除当前串
            used.remove(cur);
            // 返回当前层的结果，不能匹配到返回-1
            return min == 10 ? -1 : min + 1;
        }
        // 统计两个字符串相差的字符个数
        private int valid(String next, String cur){
            int count = 0;
            for (int i = 0; i < next.length(); i++){
                if (next.charAt(i) != cur.charAt(i)) count++;
            }
            return count;
        }


        /**
         * 使用单向BFS进行搜索
         */
        char[] keys = {'A','T','C','G'};
        public int minMutation(String start, String end, String[] bank) {
            Set<String> set = new HashSet<>();// 保存所有合格的字符串
            Collections.addAll(set, bank);
            Map<String, Integer> map = new HashMap<>();// 保存能达到字符串所需的步数
            if (!set.contains(end)) {
                return -1;// 不包含直接返回-1
            }
            map.put(start, 0);// 添加初始字符串为1
            Deque<String> deque = new ArrayDeque<>();// 使用BFS
            deque.addLast(start);
            while (!deque.isEmpty()){
                int size = deque.size();
                while (size-- > 0){
                    String poll = deque.pollFirst();
                    char[] chars = poll.toCharArray();
                    int step = map.get(poll);// 取得当前所需步数
                    for (int i = 0; i < 8; i++) {// 修改原有字符串的每一位
                        for (char c : keys){
                            if (c == chars[i]) {
                                continue;
                            }
                            char[] clone = chars.clone();
                            clone[i] = c;
                            String sub = String.valueOf(clone);
                            // 已经有过的串直接跳过
                            if (!set.contains(sub) || map.containsKey(sub)) {
                                continue;
                            }
                            // 相等直接返回
                            if (end.equals(sub)) {
                                return step + 1;
                            }
                            map.put(sub, step + 1);
                            deque.addLast(sub);
                        }
                    }
                }
            }
            return -1;
        }


        /**
         * 使用双向BFS进行搜索防止空间过大
         */
        public int minMutation2(String start, String end, String[] bank) {
            Set<String> set = new HashSet<>();
            Collections.addAll(set, bank);
            char[] chars = {'A','C','T','G'};
            HashMap<String, Integer> map1 = new HashMap<>(), map2 = new HashMap<>();
            Deque<String> deque1 = new ArrayDeque<>(), deque2 = new ArrayDeque<>();
            map1.put(start, 0);
            deque1.addLast(start);
            map2.put(end, 0);
            deque2.addLast(end);
            while (!deque1.isEmpty() && !deque2.isEmpty()){
                int t;
                if (deque1.size() <= deque2.size()) t = update(deque1, map1, map2, set, chars);
                else t = update(deque2, map2, map1, set, chars);
                if (t != -1) return t;
            }
            return -1;
        }

        /**
         * 更新较短的队列
         */
        private int update(Deque<String> deque, Map<String, Integer> cur, Map<String, Integer> other,
                           Set<String> set, char[] chars){
            int size = deque.size();
            while (size-- > 0){
                String poll = deque.pollFirst();
                char[] pollChars = poll.toCharArray();
                char[] clone = pollChars.clone();
                int step = cur.get(poll);
                for (int i = 0; i < 8; i++) {
                    for (char c : chars){
                        if (c == pollChars[i]) continue;
                        clone[i] = c;
                        String sub = String.valueOf(clone);
                        if (set.contains(sub) || cur.containsKey(sub)) continue;
                        if (other.containsKey(sub)) return other.get(sub) + step + 1;
                        cur.put(sub, step + 1);
                        deque.addLast(sub);
                    }
                }
            }
            return -1;
        }
    }
//leetcode submit region end(Prohibit modification and deletion)

}





























